iT邦幫忙

2021 iThome 鐵人賽

DAY 13
0
Modern Web

Flask系列 第 13

Day 13 實作 manage.py

  • 分享至 

  • xImage
  •  

前言

前天我們寫好了 create_app,但是還沒有人呼叫他,今天我們就要來呼叫他 (當然還不能用啦,因為藍圖都沒寫好)。

這個檔案叫做 manage.py,顧名思義,他就是用來管理的,我們可以用它來操作這個 application 及其附屬的資源,像是資料庫等等。而他是會用 flask 這個指令來操作 (跟 flask run 同一個),我們預計會加入以下幾個指令:

  • init_db 會把資料庫初始化,但如果本來就存在他不會亂動。
  • reset_db 會把資料庫整個砍掉再重建。
  • create_user 會用來新增一個使用者,當然也可以透過設定 is_admin=True 來讓它變成管理員。

manage.py

事不宜遲,我們就開始吧。但在開始之前我們要先寫一下 manage.py 的基本架構。後面我們會執行他,所以也會連帶執行到之前的 create_app,如果想看他正常執行的話,要先把那幾行藍圖的程式碼註解掉,不然會跑錯誤,畢竟我們根本沒定義過他們。


from os import getenv
from app import create_app, db

app = create_app(getenv("FLASK_ENV"))


@app.shell_context_processor
def make_shell_context():
    return globals()

我們在最一開始引入了 getenv,這是之前很熟悉的函式了。接著還有 create_appdb,後者在今天不會用到,只是先引入留著,之後會用到。

接著我們用 create_app 造出了一個 app,使用的參數是環境變數 FLASK_ENV,所以自己要先設定好這個環境變數。

最後三行是 flask 提供的一個可以方便 debug 的小工具,他可以執行 flask 然後開一個 shell 讓你跟他互動,可以查現在 app 裡面有什麼內容之類的。使用的方式也很簡單,用 flask shell 這個指令即可,他就會開一個 shell 給你用。但這時候使用他會噴錯誤,我們等等還需要設定一個環境變數來處理。要使用這個 shell 是不需要這三行的,flask 本來就可以直接用,他們的用意是把現在的環境直接複製到 shell 裡面,讓 shell 可以看的資訊更多 (整個 globals()),他這邊用到了 context 這個詞,如果深入鑽研的話會一直看到他,但在此處就先不提。可以試試看不加這三行,可以很明顯地發現如果不加然後在 shell 使用 create_app 會出現未定義。

這時候可能會想,我們根本沒有 app.run() 或是類似的函式,那他要怎麼執行?我們需要先設定 FLASK_APP 這個環境變數,它是用來告知 flask 我們的 app 在哪裡的。以此處為例,我們需要把它設為 manage.py,這樣 flask 就會知道我們的 app 就放在 manage.py 裡面,然後他就會自動自發地執行他。設定完成之後,flask shell 應該就可以使用了,可以嘗試看看。

但我們當然不是要執行 shell,我們需要讓他真的跑起來讓大家都可以用,所以我們就需要用之前用過的 flask run 來讓他跑起來,如同上面所述,它會自動去抓 app 然後讓他跑起來。

自訂指令

因為這些新指令都是有關資料庫的,所以在加入新的指令到 manage.py 之前,我們必須先寫好資料庫端的函式,基本上就是不要讓 db 離開 app/database。我們需要先多開一個 helper.py 放在 app/database,然後加入一些函式給外部引入。

from .models import db, Users


def init():
    db.create_all()


def reset():
    db.drop_all()
    db.create_all()


def add_user(username, password, email, introduction=None, is_admin=False):
    user = Users(username, password, email, introduction, is_admin)
    try:
        db.session.add(user)
        db.session.commit()
        return True
    except:
        return False

這邊加入了三個函式,我們一個一個看。記得也要在 app/database/__init__.py 加入 from .helper import init, reset, add_user

  • init 是用來初始化資料庫的,我們使用 create_all 這個函式來幫忙,他會幫我們把資料庫的 table 都建好,但如果已經存在的話,他不會把資料刪掉。
  • reset 是用來把資料刪光光並重新建好資料庫的,我們使用 drop_allcreate_all,分別會把資料都刪光及建立資料庫,所以把他們兩個一起用就可以達到重設的目的了。
  • add_user 是用來新增使用者的函式,要建立一個使用者很簡單,就只要用我們給的參數建立一個 Users 物件即可 (最前面引入了),接下來我們用 try ... except ... 來避免錯誤,沒錯誤就回傳 True,反之 False,當然可以做得更細緻 (判斷使用者名稱是否重複等等),但這邊就展示性質,不處理其他狀況。這邊我們使用 db.session.add 來把這個使用者加入到 session 裡面,接著再 db.session.commit 把這個變化寫入資料庫。

結束資料庫端的函式之後,我們可以看看 manage.py 要怎麼接。要記得最前面需要引入 from app.database import init, reset, add_user

@app.cli.command(name="init_db")
def init_db():
    init()

@app.cli.command(name="reset_db")
def reset_db():
    reset()

@app.cli.command(name="create_user")
def create_user():
    username = input("Username: ")
    password = input("Password: ")
    email = input("Email: ")
    is_admin = True if input("Is admin or not (y or n): ") == "y" else False
    if add_user(username, password, email, None, is_admin):
        print("OK")
    else:
        print("Failed")

這裡我們使用了 app.cli.command 這個裝飾器,flask 的這個部分是用 click 實作的,所以會有一些點跟他一樣,像是函式名稱是 create_user 但要呼叫的時候需要用 create-user,我自己是習慣把它改掉,使用 name 這個參數。

init_db 的部分基本上就是剛剛的 init,不管他。reset 也跟剛剛一樣,不管他。create_user 我們用 input 的方式讓人輸入資料,然後加入。這裡可以自己加入 email 的判斷、密碼長度是否過短之類的偵測,這邊就不大費周章了。

把他們加入完之後,就可以直接用 flask 來執行,我們可以先 flask init_db 讓他初始化資料庫,然後 flask add_user 新增一個使用者,沒有意外的話應該都可以跑得很順利。

References

Command Line Interface
Session Basics


上一篇
Day 12 實作資料庫
下一篇
Day 14 實作 database migration
系列文
Flask30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
tytom2005
iT邦新手 5 級 ‧ 2023-03-28 17:00:54

請問"manage.py"是放在那裏?

直接放在最外層的目錄就好
建議可以直接看 repo 會比較清楚

我要留言

立即登入留言